package com.jh.wdbc.service;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.jh.wdbc.model.ConnectionInfo;
import com.nhncorp.dbpropmanager.model.PropertyModel;
import com.nhnent.dbpmdecryptor.DataSourcePropertyEncryptor;

@Service
public class DataBaseConnectionService {

	private HashMap<String, ConnectionInfo> connMap;
	private List<String> history;
	private DataSourcePropertyEncryptor encryptor = new DataSourcePropertyEncryptor();

	public List<String> getHistory() {
		return history;
	}

	public DataBaseConnectionService() {
		connMap = new HashMap<String, ConnectionInfo>();
		history = new ArrayList<String>();
		readHistory();
	}

	public void setConnectionInfo(String name) throws Exception {
		String names[] = name.split("\\.");
		StringBuilder sb = new StringBuilder(names[1]);
		for (int i = 2; i < names.length; i++) {
			sb.append(".").append(names[i]);
		}
		setConnectionInfo(names[0], sb.toString());
	}

	public void setConnectionInfo(String phase, String dbid) throws Exception {
		ConnectionInfo connectionInfo = getConnectionInfoFromDBPM(phase, dbid);
		if (connectionInfo == null) {
			return;
		}

		ConnectionFactory.setConnection(connectionInfo);
		connMap.put(connectionInfo.getName(), connectionInfo);
		if (!history.contains(connectionInfo.getName())) {
			history.add(connectionInfo.getName());
			writeHistory();
		}
	}

	public ConnectionInfo getConnectionInfoFromDBPM(String phase, String dbid) throws Exception {
		try {
			String dbName = phase + "." + dbid;

			PropertyModel dbpm = DBPMUtil.getDBProperty(phase, dbid);
			String driver = dbpm.getDriver();
			String url = dbpm.getUrl();
			String username = dbpm.getUsername();
			String passwd = dbpm.getPassword();

			url = encryptor.decrypt(url);
			username = encryptor.decrypt(username);
			passwd = encryptor.decrypt(passwd);

			ConnectionInfo connectionInfo = new ConnectionInfo();

			connectionInfo.setName(dbName);
			connectionInfo.setDbType(driver);
			connectionInfo.setUser(username);
			connectionInfo.setPasswd(passwd);
			connectionInfo.setUrl(url);
			connectionInfo.setRawEncoding(dbpm.getEncoding());

			return connectionInfo;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	private void writeHistory() {
		String path = System.getProperty("catalina.base");
		try {
			File file = new File(path + "/.dbwchist");
			BufferedWriter out = new BufferedWriter(new FileWriter(file, false));
			for (String name : history) {
				out.write(name);
				out.newLine();
			}
			out.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void readHistory() {
		String path = System.getProperty("catalina.base");
		try {
			BufferedReader br = new BufferedReader(new FileReader(path + "/.dbwchist"));
			String line = br.readLine();
			while (line != null) {
				history.add(line.trim());
				line = br.readLine();
			}
			br.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public ConnectionInfo getConnectionInfo(String name) throws Exception {
		ConnectionInfo connectionInfo = connMap.get(name);
		if (connectionInfo == null) {
			setConnectionInfo(name);
			connectionInfo = connMap.get(name);
		}

		if (!checkConnection(connectionInfo)) {
			reconnect(connectionInfo);
		}
		return connectionInfo;
	}

	private void reconnect(ConnectionInfo connectionInfo) {
		try {
			connectionInfo.getConnection().close();
		} catch (SQLException e) {
		}
		ConnectionFactory.setConnection(connectionInfo);
	}

	private boolean checkConnection(ConnectionInfo connectionInfo) {
		try {
			String query = "select 1";
			if (connectionInfo.getDbType().contains("ORACLE")) {
				query = "select 1 from dual";
			}

			Statement stmt = connectionInfo.getConnection().createStatement();
			stmt.executeQuery(query);
			stmt.close();
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	public void destroy() {
		if (!connMap.isEmpty()) {
			for (String key : connMap.keySet()) {
				try {
					connMap.get(key).getConnection().close();
				} catch (SQLException e) {

				}
				connMap.remove(key);
			}
		}
	}

	public Set<String> getConnectionInfoList() {
		return this.connMap.keySet();
	}
}
